package com.doubao.wechat.util;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;
import org.springframework.util.Assert;

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.Signature;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;

/**
 * 微信支付签名工具类
 */
@Slf4j
public class WxPaySignatureUtil {

    /**
     * 读取私钥文件
     *
     * @param filePath 私钥文件路径
     * @return 私钥字符串
     */
    public static String readPrivateKey(String filePath) {
        try {
            // 判断是否从classpath加载
            if (filePath.startsWith("classpath:")) {
                String classPathFile = filePath.substring("classpath:".length());
                try (InputStream is = WxPaySignatureUtil.class.getClassLoader().getResourceAsStream(classPathFile)) {
                    if (is == null) {
                        throw new RuntimeException("无法从classpath加载私钥文件: " + classPathFile);
                    }
                    return readPrivateKeyFromStream(is);
                }
            } else {
                // 原有的从文件系统加载的逻辑
                try (BufferedReader br = new BufferedReader(new FileReader(filePath))) {
                    String line;
                    StringBuilder sb = new StringBuilder();
                    while ((line = br.readLine()) != null) {
                        if (line.startsWith("-----BEGIN") || line.startsWith("-----END")) {
                            continue;
                        }
                        sb.append(line);
                    }
                    return sb.toString();
                }
            }
        } catch (Exception e) {
            throw new RuntimeException("读取私钥文件失败: " + e.getMessage(), e);
        }
    }

    /**
     * 从输入流读取私钥
     */
    private static String readPrivateKeyFromStream(InputStream inputStream) throws IOException {
        try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) {
            StringBuilder sb = new StringBuilder();
            String line;
            while ((line = reader.readLine()) != null) {
                if (line.startsWith("-----BEGIN") || line.startsWith("-----END")) {
                    continue;
                }
                sb.append(line);
            }
            return sb.toString();
        }
    }

    /**
     * 获取私钥对象
     *
     * @param privateKey 私钥字符串
     * @return 私钥对象
     */
    public static PrivateKey getPrivateKey(String privateKey) {
        try {
            privateKey = privateKey.replaceAll("\\s+", "");
            byte[] keyBytes = Base64.decodeBase64(privateKey);
            PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            return keyFactory.generatePrivate(pkcs8KeySpec);
        } catch (Exception e) {
            throw new RuntimeException("获取私钥对象失败", e);
        }
    }
    /**
     * 生成签名
     *
     * @param method     请求方法
     * @param url        请求地址
     * @param timestamp  时间戳
     * @param nonceStr   随机字符串
     * @param body       请求体
     * @param privateKey 私钥对象
     * @return 签名
     */
    public static String generateSignature(String method, String url, String timestamp,
                                           String nonceStr, String body, PrivateKey privateKey) {
        try {
            // 确保URL以斜杠开头 - 微信支付API签名要求URL必须以斜杠开头
            if (!url.startsWith("/") && !url.startsWith("http")) {
                url = "/" + url;
            }
            
            // 如果URL包含完整域名，只取路径部分
            if (url.startsWith("http")) {
                java.net.URL parsedUrl = new java.net.URL(url);
                url = parsedUrl.getPath();
                if (parsedUrl.getQuery() != null && !parsedUrl.getQuery().isEmpty()) {
                    url = url + "?" + parsedUrl.getQuery();
                }
            }
            
            String signatureStr = method + "\n" + url + "\n" + timestamp + "\n" + nonceStr + "\n" + body + "\n";
            
            // 添加日志输出签名原文
            log.debug("微信支付API签名原文: \n{}", signatureStr);
            
            Signature sign = Signature.getInstance("SHA256withRSA");
            sign.initSign(privateKey);
            sign.update(signatureStr.getBytes(StandardCharsets.UTF_8));
            return Base64.encodeBase64String(sign.sign());
        } catch (Exception e) {
            throw new RuntimeException("生成签名失败", e);
        }
    }

    /**
     * 生成JSAPI支付签名
     *
     * @param appId      应用ID
     * @param timeStamp  时间戳
     * @param nonceStr   随机字符串
     * @param packageStr 订单详情扩展字符串
     * @param privateKey 私钥对象
     * @return 签名
     */
    public static String generateJsapiSignature(String appId, String timeStamp,
                                                String nonceStr, String packageStr, PrivateKey privateKey) {
        try {
            String signatureStr = appId + "\n" + timeStamp + "\n" + nonceStr + "\n" + packageStr + "\n";
            Signature sign = Signature.getInstance("SHA256withRSA");
            sign.initSign(privateKey);
            sign.update(signatureStr.getBytes(StandardCharsets.UTF_8));
            return Base64.encodeBase64String(sign.sign());
        } catch (Exception e) {
            throw new RuntimeException("生成JSAPI支付签名失败", e);
        }
    }

    /**
     * 生成微信支付认证头
     *
     * @param method     请求方法
     * @param url        请求地址
     * @param timestamp  时间戳
     * @param nonceStr   随机字符串
     * @param body       请求体
     * @param mchId      商户号
     * @param serialNo   证书序列号
     * @param privateKey 私钥对象
     * @return 认证头
     */
    public static String generateAuthorizationHeader(String method, String url, String timestamp,
                                                     String nonceStr, String body, String mchId,
                                                     String serialNo, PrivateKey privateKey) {
        String signature = generateSignature(method, url, timestamp, nonceStr, body, privateKey);
        return "WECHATPAY2-SHA256-RSA2048 " +
                "mchid=\"" + mchId + "\"," +
                "nonce_str=\"" + nonceStr + "\"," +
                "signature=\"" + signature + "\"," +
                "timestamp=\"" + timestamp + "\"," +
                "serial_no=\"" + serialNo + "\"";
    }

    /**
     * 生成微信支付V3接口请求用随机字符串
     *
     * @return 随机字符串
     */
    public static String generateNonceStr() {
        return java.util.UUID.randomUUID().toString().replaceAll("-", "");
    }

    /**
     * 检查并输出签名结果，用于调试
     *
     * @param method     请求方法
     * @param url        请求地址
     * @param timestamp  时间戳
     * @param nonceStr   随机字符串
     * @param body       请求体
     * @param privateKey 私钥对象
     * @return 签名结果信息
     */
    public static String verifyAndDebugSignature(String method, String url, String timestamp,
                                                String nonceStr, String body, PrivateKey privateKey) {
        // 确保URL以斜杠开头
        if (!url.startsWith("/") && !url.startsWith("http")) {
            url = "/" + url;
        }
        
        // 如果URL包含完整域名，只取路径部分
        if (url.startsWith("http")) {
            try {
                java.net.URL parsedUrl = new java.net.URL(url);
                url = parsedUrl.getPath();
                if (parsedUrl.getQuery() != null && !parsedUrl.getQuery().isEmpty()) {
                    url = url + "?" + parsedUrl.getQuery();
                }
            } catch (Exception e) {
                log.error("解析URL失败", e);
            }
        }
        
        String signatureStr = method + "\n" + url + "\n" + timestamp + "\n" + nonceStr + "\n" + body + "\n";
        log.info("=================== 微信支付签名验证 ===================");
        log.info("原始URL: {}", url);
        log.info("签名原文:\n{}", signatureStr);
        try {
            Signature sign = Signature.getInstance("SHA256withRSA");
            sign.initSign(privateKey);
            sign.update(signatureStr.getBytes(StandardCharsets.UTF_8));
            String signature = Base64.encodeBase64String(sign.sign());
            log.info("签名结果: {}", signature);
            return signature;
        } catch (Exception e) {
            log.error("签名验证失败", e);
            return null;
        }
    }
}